package net.wicp.tams.plugin.task.project;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.tree.ConfigurationNode;
import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;

import javassist.CtClass;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.connector.XmlUtil;
import net.wicp.tams.common.tracer.instrumentation.AbsHandler;
import net.wicp.tams.common.tracer.instrumentation.IClassEnhance;
import net.wicp.tams.plugin.TaskAssit;
import net.wicp.tams.plugin.constant.SupClass;

/***
 * 添加trace代码
 * 
 * @author andy.zhou
 *
 */
@Mojo(name = "trace", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class Trace extends AbstractMojo {

	private static final String _trace = "trace";
	private static final String _super = "super";
	private static final String _include = "include";
	private static final String _exclude = "exclude";

	// 属性名
	private static final String _enclassname = "enclassname";
	// private static final String _classname = "classname";
	// private static final String _method = "method";
	// private static final String _params = "params";

	private List<String> hasClassName = new ArrayList<>();

	// 需要加入缓存的配置文件 project.build.resources
	@Parameter(defaultValue = "${project.build.outputDirectory}/trace.xml", property = "config", required = true)
	private File config;

	// class产生的路径
	@Parameter(defaultValue = "${project.build.outputDirectory}", property = "classroot", required = true)
	private File classroot;

	// 项目目录
	@Parameter(defaultValue = "${project.basedir}/", required = true)
	private String basedir;

	@Parameter(defaultValue = "${project}", required = true)
	private MavenProject project;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		try {
			TaskAssit.addProjectClassPath(project, AbsHandler.pool);
		} catch (Exception e) {
			throw new MojoFailureException("-----------------加classpath错误---------", e);
		}

		// 临时存放，成功后会把它放到classroot
		File homeDir = new File(String.format("%s/%s/%s/%s", System.getProperty("user.home"), "/.tams/", "trace",
				project.getArtifactId()));
		try {
			if (homeDir.exists()) {
				FileUtils.forceDelete(homeDir);
			}
			homeDir.mkdirs();

			XMLConfiguration xml = null;
			try {
				xml = new XMLConfiguration(config);
				xml.setEncoding("UTF-8");
				xml.setExpressionEngine(new XPathExpressionEngine());// 设置Xpath解析器
			} catch (Exception e) {
				throw new MojoFailureException("读缓存配置错误,默认文件为trace.xml.");
			}
			ConfigurationNode node = xml.getRoot();
			if (StringUtils.isBlank(node.getName()) || "null".equals(node.getName())) {
				getLog().info("------------------------trace---end------没有找到合适的配置文件-----------");
				return;
			}

			if (!_trace.equalsIgnoreCase(node.getName())) {
				throw new MojoFailureException(String.format("配置文件错误:根必须为%s", _trace));
			}
			getLog().info("------------------------加强trace的字节码---begin---------------------");
			List<ConfigurationNode> cachelist = node.getChildren(_super);
			if (CollectionUtils.isEmpty(cachelist)) {
				return;
			}
			for (int i = 0; i < cachelist.size(); i++) {
				ConfigurationNode superNode = cachelist.get(i);
				final List<String> includeList = XmlUtil.getChildValues(superNode, _include);
				final List<String> excludeList = XmlUtil.getChildValues(superNode, _exclude);
				String enclassname = XmlUtil.findValueByAttrName(superNode, _enclassname);
				SupClass supClass = null;
				if (StringUtil.isNotNull(enclassname)) {
					supClass = SupClass.getByName(enclassname);
					if (supClass == null) {
						throw new MojoFailureException("-----------------不支持的父类[" + enclassname + "]---------");
					}
				}
				Set<String> sublist = TaskAssit.findSubClassByProject(getLog(), supClass.getClassName(), project,
						AbsHandler.pool);
				if (CollectionUtils.isNotEmpty(includeList)) {
					CollectionUtils.filter(sublist, new Predicate() {

						@Override
						public boolean evaluate(Object object) {
							String className = String.valueOf(object);
							if (includeList.contains(className)) {
								return true;
							} else {
								return false;
							}

						}
					});
				}
				if (CollectionUtils.isNotEmpty(excludeList)) {
					CollectionUtils.filter(sublist, new Predicate() {

						@Override
						public boolean evaluate(Object object) {
							String className = String.valueOf(object);
							if (excludeList.contains(className)) {
								return false;
							} else {
								return true;
							}

						}
					});
				}

				if (CollectionUtils.isEmpty(sublist)) {
					continue;
				}

				for (String subClassName : sublist) {
					IClassEnhance enhance = (IClassEnhance) Class.forName(supClass.getEnhanceClassName())
							.getConstructor(String.class).newInstance(subClassName);
					if (StringUtils.isEmpty(subClassName) || hasClassName.contains(subClassName)) {
						continue;
					}
					CtClass cc = enhance.transformed();
					if (cc != null) {
						cc.writeFile(homeDir.getPath());
					}
					hasClassName.add(subClassName);
				}
			}

		} catch (Exception e) {
			throw new MojoFailureException("-----------------加强trace的字节码错误---------", e);
		}
		try {
			FileUtils.copyDirectory(homeDir, classroot, true);
			getLog().info("------------------------加强trace的字节码---end---------------------");
		} catch (IOException e) {
			throw new MojoFailureException("-----------------加强复制文件出错---------", e);
		}

	}

}
